#include "ModelLoader.h"
#include <filesystem>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/Importer.hpp>
#include <assimp/postprocess.h>
#include <fstream>
#include <SERender/Resourses/Loader/VertexArrayLoader.h>
#include <SERender/Resourses/Loader/TextureLoader.h>
#include <SERender/Resourses/Mesh.h>
#include <SECore/Manager.h>
#include <algorithm>   // for std::for_each
#include <execution>   // for std::execution::par

#include <SEngine/Render/Entity/BoundingBox.h>

#include <SEngine/Render/Entity/Octree.h>
#include <SEngine/Render/Entity/OctreeNode.h>


namespace SEngine
{
    // 计算点P到三角形ABC的最近距离
    float point_to_triangle_distance(const glm::vec3& P, const glm::vec3& A, 
        const glm::vec3& B, const glm::vec3& C) {

        // 三角形的边向量
        glm::vec3 AB = B - A;
        glm::vec3 AC = C - A;
        glm::vec3 AP = P - A;

        // 计算重心坐标参数
        float d1 = glm::dot(AB, AP);
        float d2 = glm::dot(AC, AP);

        // 检查P是否在顶点A的Voronoi区域外
        if (d1 <= 0.0f && d2 <= 0.0f) {
            return glm::length(AP); // 最近点是A
        }

        // 检查P是否在顶点B的Voronoi区域
        glm::vec3 BP = P - B;
        float d3 = glm::dot(AB, BP);
        float d4 = glm::dot(AC, BP);

        if (d3 >= 0.0f && d4 <= d3) {
            return glm::length(BP); // 最近点是B
        }

        // 检查P是否在边AB的Voronoi区域内
        float vc = d1 * d4 - d3 * d2;
        if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) {
            float v = d1 / (d1 - d3);
            glm::vec3 closest_point = A + v * AB;
            return glm::length(P - closest_point);
        }

        // 检查P是否在顶点C的Voronoi区域
        glm::vec3 CP = P - C;
        float d5 = glm::dot(AB, CP);
        float d6 = glm::dot(AC, CP);

        if (d6 >= 0.0f && d5 <= d6) {
            return glm::length(CP); // 最近点是C
        }

        // 检查P是否在边AC的Voronoi区域内
        float vb = d5 * d2 - d1 * d6;
        if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) {
            float w = d2 / (d2 - d6);
            glm::vec3 closest_point = A + w * AC;
            return glm::length(P - closest_point);
        }

        // 检查P是否在边BC的Voronoi区域内
        float va = d3 * d6 - d5 * d4;
            if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) {
            float w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
            glm::vec3 closest_point = B + w * (C - B);
            return glm::length(P - closest_point);
        }

        // P在三角形内部，计算到平面的距离
        float denom = 1.0f / (va + vb + vc);
        float v = vb * denom;
        float w = vc * denom;

        glm::vec3 closest_point = A + v * AB + w * AC;
        return glm::length(P - closest_point);
    }

    // 判断点是否在网格内部（使用射线法）
    bool is_point_inside_mesh(const glm::vec3& point, const std::vector<glm::vec3>& vertices, 
        const std::vector<glm::ivec3>& faces) {
        int intersection_count = 0;
        glm::vec3 ray_dir(1.0f, 0.0f, 0.0f); // 沿x轴发射射线

        for (const auto& face : faces) {
            const glm::vec3& v0 = vertices[face.x];
            const glm::vec3& v1 = vertices[face.y];
            const glm::vec3& v2 = vertices[face.z];

            // 简单的射线-三角形相交测试
            glm::vec3 edge1 = v1 - v0;
            glm::vec3 edge2 = v2 - v0;
            glm::vec3 h = glm::cross(ray_dir, edge2);
            float a = glm::dot(edge1, h);

            if (std::abs(a) < 1e-6f) continue; // 平行

            float f = 1.0f / a;
            glm::vec3 s = point - v0;
            float u = f * glm::dot(s, h);

            if (u < 0.0f || u > 1.0f) continue;

            glm::vec3 q = glm::cross(s, edge1);
            float v = f * glm::dot(ray_dir, q);

            if (v < 0.0f || u + v > 1.0f) continue;

            float t = f * glm::dot(edge2, q);
            if (t > 1e-6f) {
                intersection_count++;
            }
        }
        return (intersection_count % 2) == 1;
    }


    template<int MaxL>
    Octree& GenOctree(const std::vector<glm::vec3> & vs, const std::vector<glm::ivec3> & fs, Octree& tree) {
        struct Mesh {
            std::vector<glm::vec3> vs;
            std::vector<glm::ivec3> fs;
        };
        BoundingBox box;
        Mesh mesh;
        for (auto & face : fs) {
            box.min = glm::min(vs[face.x], box.min);
            box.min = glm::min(vs[face.y], box.min);
            box.min = glm::min(vs[face.z], box.min);

            box.max = glm::max(vs[face.x], box.max);
            box.max = glm::max(vs[face.y], box.max);
            box.max = glm::max(vs[face.z], box.max);

            mesh.vs.emplace_back(vs[face.x]);
            mesh.vs.emplace_back(vs[face.y]);
            mesh.vs.emplace_back(vs[face.z]);
            mesh.fs.emplace_back(face);
        }
        int minVs = glm::max(5, static_cast<int>(glm::log(vs.size())));
        auto & node = tree.CreateNode(box);
        node.AddDescriptor<Mesh>(std::move(mesh));
        std::function<void(OctreeNode&, int)> GenOctreeImpl = [&GenOctreeImpl, minVs](OctreeNode & curNode, int curLevel) {
            auto & mesh = curNode.GetDescriptor<Mesh>()->get();
            if (mesh.vs.size() < minVs || curLevel >= MaxL) {
                return;
            }
            curNode.Split();
            curNode.ForeachChildren([&](OctreeNode & subNode) {
                Mesh subMesh;
                for (auto & face : mesh.fs) {
                    if (subNode.GetBoundingBox().Contained(mesh.vs[face.x]) ||
                        subNode.GetBoundingBox().Contained(mesh.vs[face.y]) ||
                        subNode.GetBoundingBox().Contained(mesh.vs[face.z])) 
                    {
                        subMesh.vs.emplace_back(mesh.vs[face.x]);
                        subMesh.vs.emplace_back(mesh.vs[face.y]);
                        subMesh.vs.emplace_back(mesh.vs[face.z]);
                        subMesh.fs.emplace_back(face);
                    }
                }
                subNode.AddDescriptor<Mesh>(std::move(subMesh));
                GenOctreeImpl(subNode, curLevel + 1);
            });
            curNode.Clear();
        };
        GenOctreeImpl(node, 0);
        return tree;
    }

    void ModelLoader::Load(const std::string &fileName)
    {
        std::ifstream file(fileName);
        if (file.fail()) {
            throw std::runtime_error("Model file: " + fileName + " can not open");
        }
        file.close();
        std::filesystem::path path(fileName);
        m_directory = path.parent_path().string();
        m_importer = std::make_shared<Assimp::Importer>();
        int modelParserFlags = 0; 
        modelParserFlags |= aiProcess_Triangulate;
        modelParserFlags |= aiProcess_GenSmoothNormals;
        modelParserFlags |= aiProcess_FlipUVs;
        // modelParserFlags |= aiProcess_OptimizeMeshes;
        // modelParserFlags |= aiProcess_OptimizeGraph;
        // modelParserFlags |= aiProcess_FindInstances;
        // modelParserFlags |= aiProcess_CalcTangentSpace;
        // modelParserFlags |= aiProcess_JoinIdenticalVertices;
        // modelParserFlags |= aiProcess_Debone;
        // modelParserFlags |= aiProcess_FindInvalidData;
        // modelParserFlags |= aiProcess_ImproveCacheLocality;
        // modelParserFlags |= aiProcess_GenUVCoords;
        // modelParserFlags |= aiProcess_GlobalScale;
        // modelParserFlags |= aiProcess_PreTransformVertices;
        m_scene = m_importer->ReadFile(fileName, modelParserFlags);
        if (!m_scene) {
            throw std::runtime_error("Load Model: " + fileName + " fiald");
        }
    }

    std::unique_ptr<MeshFilter> ModelLoader::ExportMeshFilter()
    {
        auto meshFilter = std::make_unique<MeshFilter>();
        this->ForeachMeshes([&meshFilter, this](const std::string & name, aiMesh * aimesh) {
            /**
                layout (location = 0) in vec3 aPos;
                layout (location = 1) in vec2 aCoord;
                layout (location = 2) in vec3 aNormal;
                layout (location = 3) in vec3 tangent;
                layout (location = 4) in vec3 bitangent;
            */
            VertexArrayLoader<3, 2, 3, 3, 3> loader(std::make_unique<CurrentDrive>());
            for (size_t i = 0; i < aimesh->mNumVertices; ++i) {
                auto vertex = aimesh->mVertices[i];
                auto normal = aimesh->mNormals[i];
                auto tangents = CalcTangent(glm::vec3{normal.x, normal.y, normal.z});
                auto biTangents = CalcBitangent(glm::vec3{normal.x, normal.y, normal.z}, tangents);
                auto uv = aimesh->mTextureCoords[0] ? aimesh->mTextureCoords[0][i] : aiVector3f{0.0f, 0.0f, 0.0f};
                loader.PushVertex({ vertex.x, vertex.y, vertex.z, 
                                    uv.x, uv.y,
                                    normal.x, normal.y, normal.z, 
                                    tangents.x, tangents.y, tangents.z,
                                    biTangents.x, biTangents.y, biTangents.z
                                });
            }
            for (size_t i = 0; i < aimesh->mNumFaces; ++i) {
                auto face = aimesh->mFaces[i];
                std::array<uint32_t, 3> indexArray;
                for (size_t j = 0; j < face.mNumIndices; ++j) {
                    indexArray[j] = face.mIndices[j];
                }
                loader.PushFace(std::move(indexArray));
            }
            meshFilter->AddMesh(name, std::make_unique<Mesh>(loader.CreateVertexArray()));
        });

        // std::vector<glm::vec3> v;
        // std::vector<glm::ivec3> f;
        // ForeachMeshes([&v, &f](const std::string &name, aiMesh *aimesh) {
        //     for (size_t i = 0; i < aimesh->mNumVertices; ++i) {
        //         v.emplace_back(glm::vec3{aimesh->mVertices[i].x, aimesh->mVertices[i].y, aimesh->mVertices[i].z});
        //     }
        //     for (size_t i = 0; i < aimesh->mNumFaces; ++i) {
        //         f.emplace_back(glm::ivec3{(const int)aimesh->mFaces[i].mIndices[0], (const int)aimesh->mFaces[i].mIndices[1], (const int)aimesh->mFaces[i].mIndices[2]});
        //     }
        // });
        // Octree tree;
        // GenOctree<2>(v, f, tree);
        // meshFilter->AddDescriptor<Octree>(std::move(tree));
        return meshFilter;
    }

    std::unique_ptr<MaterialFilter> ModelLoader::ExportMaterialFilter()
    {
        auto materialFilter = std::make_unique<MaterialFilter>();
        this->ForeachMaterials([&materialFilter, this](const std::string &name, aiMaterial *material) {
            materialFilter->AddMaterial(name, m_material);
        });
        return materialFilter;
    }

    std::unique_ptr<SDF> ModelLoader::ExportSDF()
    {
        std::vector<glm::vec3> v;
        std::vector<glm::ivec3> f;
        ForeachMeshes([&v, &f](const std::string &name, aiMesh *aimesh) {
            for (size_t i = 0; i < aimesh->mNumVertices; ++i) {
                v.emplace_back(glm::vec3{aimesh->mVertices[i].x, aimesh->mVertices[i].y, aimesh->mVertices[i].z});
            }
            for (size_t i = 0; i < aimesh->mNumFaces; ++i) {
                f.emplace_back(glm::ivec3{(const int)aimesh->mFaces[i].mIndices[0], (const int)aimesh->mFaces[i].mIndices[1], (const int)aimesh->mFaces[i].mIndices[2]});
            }
        });
        Octree tree;
        GenOctree<2>(v, f, tree);
        return std::unique_ptr<SDF>();
    }

    void ModelLoader::ForeachMeshes(std::function<void(const std::string &name, aiMesh *mesh)> &&fn)
    {
        std::function<void(const aiScene *scene, aiNode *node)> ForeachNode = [&](const aiScene *scene, aiNode *node) {
            for (size_t i = 0; i < node->mNumChildren; ++i) {
                ForeachNode(scene, node->mChildren[i]);
            }
            for (size_t i = 0; i < node->mNumMeshes; ++i) {
                aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
                fn(node->mName.C_Str(), mesh);
            }
        };
        ForeachNode(m_scene, m_scene->mRootNode);
    }

    void ModelLoader::ForeachMaterials(std::function<void(const std::string &name, aiMaterial *material)> &&fn)
    {
        std::function<void(const aiScene *scene, aiNode *node)> ForeachNode = [&](const aiScene *scene, aiNode *node) {
            for (size_t i = 0; i < node->mNumChildren; ++i) {
                ForeachNode(scene, node->mChildren[i]);
            }
            for (size_t i = 0; i < node->mNumMeshes; ++i) {
                aiMesh * mesh = scene->mMeshes[node->mMeshes[i]];
                if (mesh->mMaterialIndex >= 0) {
                    fn(node->mName.C_Str(), scene->mMaterials[mesh->mMaterialIndex]);
                }
            }
        };
        ForeachNode(m_scene, m_scene->mRootNode);
    }

    glm::vec3 ModelLoader::CalcTangent(glm::vec3 normal)
    {
        glm::vec3 up{0.0f, 1.0f, 0.0f};
        if (abs(glm::dot(normal, up)) > 0.999f) {
            up = glm::vec3{0.0f, 0.0f, 1.0f};
        }
        return glm::normalize(glm::cross(normal, up));
    }

    glm::vec3 ModelLoader::CalcBitangent(glm::vec3 normal, glm::vec3 tangent)
    {
        return glm::normalize(glm::cross(normal, tangent));
    }

} // namespace SEngine
